///	<summary>
///	  Unit testing for libraries in DAttributes.Ini unit
///	</summary>
///	<remarks>
///	  Uses DUnit testing framework
///	</remarks>
unit DAttributes.WinRegistry.Test;

(*******************************************************************************
  MIT License

  Copyright (c) 2013 - Luthfi Harahap

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE
*******************************************************************************)

interface

uses
  Windows
  , SysUtils
  , Classes
  , Registry
  , TestFramework
  , DAttributes
  , DAttributes.WinRegistry
  , DAttributes.Test
  ;

const
  ROOT1      = HKEY_CURRENT_USER;
  ROOT1_NAME = 'HKCU';
  ROOT2      = HKEY_LOCAL_MACHINE;
  ROOT2_NAME = 'HKLM';
  REGKEY_01  = 'SOFTWARE\DAttributes 0.1b';
  REGKEY_02  = 'SOFTWARE\Luthfi\DAttributes\Ver 0.1b\Test 1';

  MAINROOTKEY= ROOT1;
  MAINROOTKEY_NAME = ROOT1_NAME;
  MAINREGKEY = REGKEY_01;

  PROPERTY1_NAME = 'Property1';
  PROPERTY1_ROOTKEY = ROOT1;
  PROPERTY1_ROOTKEYNAME = ROOT1_NAME;
  PROPERTY1_REGKEY      = REGKEY_01;
  PROPERTY1_DEFAULT_VALUE = 45238;
  PROPERTY1_TEST_VALUE    = 22356;

  PROPERTY2_NAME = 'Prop02';
  PROPERTY2_ROOTKEY = ROOT2;
  PROPERTY2_ROOTKEYNAME = ROOT2_NAME;
  PROPERTY2_REGKEY      = REGKEY_02;
  PROPERTY2_DEFAULT_VALUE = 'Prop2 DefaultValue';
  PROPERTY2_TEST_VALUE    = 'TEST value FOR PropERTY 02 TEST VALUE';

  PROPERTY3_NAME = 'Prop_3';
  PROPERTY3_ROOTKEY = 0;  // should follow main root key
  PROPERTY3_ROOTKEYNAME = ''; // should follow main root key
  PROPERTY3_REGKEY      = ''; // should follow main registry key
  PROPERTY3_DEFAULT_VALUE = 3243.544;
  PROPERTY3_TEST_VALUE    = 0.12345;

  PROPERTY4_NAME = 'Prop004';
  PROPERTY4_ROOTKEY = 0;  // should follow main root key
  PROPERTY4_ROOTKEYNAME = ''; // should follow main root key
  PROPERTY4_REGKEY      = ''; // should follow main registry key
  PROPERTY4_TEST_VALUE    = 491331.19645;

  PROPERTY5_NAME = 'Prop5';
  PROPERTY5_ROOTKEY = 0;  // should follow main root key
  PROPERTY5_ROOTKEYNAME = ''; // should follow main root key
  PROPERTY5_REGKEY      = ''; // should follow main registry key
  PROPERTY5_TEST_VALUE    = 3232.727234;

  READONLYPROPERTY1_NAME = 'ReadOnlyProp1';
  READONLYPROPERTY1_ROOTKEY = ROOT2;
  READONLYPROPERTY1_ROOTKEYNAME = ROOT2_NAME;
  READONLYPROPERTY1_REGKEY = REGKEY_02;
  READONLYPROPERTY1_DEFAULT_VALUE = 101010;
  READONLYPROPERTY1_TEST_VALUE = 22222;

type
  ///	<summary>
  ///	  Dummy class for testing custom attributes
  ///	</summary>
  [RegRootKey(MAINROOTKEY)]
  [RegKey(MAINREGKEY)]
  TRegistryStoreTestClass=class
  private
    FProperty1: Integer;
    FProp02: string;
    FProp_3: Double;
    FProp004: Double;
    FProp5: Double;
    FReadOnlyProp1: Int64;
  public
    constructor Create;

    [RegValue(PROPERTY1_ROOTKEYNAME + '\' + PROPERTY1_REGKEY + '\' + PROPERTY1_NAME, PROPERTY1_DEFAULT_VALUE)]
    property Property1: Integer read FProperty1 write FProperty1;
    [RegValue(PROPERTY2_ROOTKEYNAME + '\' + PROPERTY2_REGKEY + '\' + PROPERTY2_NAME, PROPERTY2_DEFAULT_VALUE)]
    property Prop02: string read FProp02 write FProp02;
    [RegValue(PROPERTY3_ROOTKEYNAME + '\' + PROPERTY3_REGKEY + '\' + PROPERTY3_NAME, PROPERTY3_DEFAULT_VALUE)]
    property Prop_3: Double read FProp_3 write FProp_3;

    [RegValue(PROPERTY4_ROOTKEYNAME + '\' + PROPERTY4_REGKEY + '\' + PROPERTY4_NAME, vtFloat)]
    property Prop004: Double read FProp004 write FProp004;
    [RegValue(PROPERTY5_ROOTKEYNAME + '\' + PROPERTY5_REGKEY + '\' + PROPERTY5_NAME, vtFloat)]
    property Prop5: Double read FProp5 write FProp5;

    [RegValue(READONLYPROPERTY1_ROOTKEYNAME + '\' + READONLYPROPERTY1_REGKEY + '\' + READONLYPROPERTY1_NAME
     , READONLYPROPERTY1_DEFAULT_VALUE)]
    property ReadOnlyProp1: Int64 read FReadOnlyProp1;
  end;

  ///	<summary>
  ///	  Unit tests for IniSection attribute class
  ///	</summary>
  TRegValueTest=class(TDefaultValueTest)
  protected
    TestItem: RegValue;
    procedure SetUp; override;
    procedure TearDown; override;

    class function TestedDefaultValueClass: DefaultValueClass; override;
    function OpenRegistry(const ARoot: HKEY; const ARegKey: string): TRegistry;
  published
    procedure Test_Create;
    procedure Test_Reset;
    procedure Test_StoreDefaults;
    procedure Test_StoreTestValues;
    procedure Test_Loading;
  end;

  ///	<summary>
  ///	  Unit tests for IniValue attribute class
  ///	</summary>
  TRegRootTest=class(TTestCase)
  published
    procedure Test_Create;
  end;

implementation

uses
  Rtti
  , TypInfo
  ;

function GetIniFilename: string;
begin
  Result := ExtractFilePath(ParamStr(0)) + 'test.ini';
end;

{ TIniSectionTest }

function TRegValueTest.OpenRegistry(const ARoot: HKEY;
  const ARegKey: string): TRegistry;
begin
  Result := TRegistry.Create;
  try
    if ARoot <> 0 then
    begin
      Result.RootKey := ARoot;
      if ARegKey<>'' then
        if not Result.OpenKeyReadOnly(ARegKey) then
          raise Exception.Create('Can not open registry key');
    end;
  except
    Result.Free;
    raise;
  end;
end;

procedure TRegValueTest.SetUp;
begin
  inherited;
end;

procedure TRegValueTest.TearDown;
begin
  inherited;
end;

class function TRegValueTest.TestedDefaultValueClass: DefaultValueClass;
begin
  Result := RegValue;
end;

procedure TRegValueTest.Test_Create;
var
  vRegValue: RegValue;
begin
  vRegValue := RegValue.Create('HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\MyAppl', vtInteger);
  try
    Check(vRegValue.Root = HKEY_CURRENT_USER);
    Check(vRegValue.Key = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Run');
    Check(vRegValue.Name = 'MyAppl');
  finally
    vRegValue.Free;
  end;

  vRegValue := RegValue.Create('MyAppl001', vtInteger);
  try
    Check(vRegValue.Root = 0);
    Check(vRegValue.Key = '');
    Check(vRegValue.Name = 'MyAppl001');
  finally
    vRegValue.Free;
  end;

  vRegValue := RegValue.Create('\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\MyAppl002', vtInteger);
  try
    Check(vRegValue.Root = 0);
    Check(vRegValue.Key = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Run');
    Check(vRegValue.Name = 'MyAppl002');
  finally
    vRegValue.Free;
  end;

  vRegValue := RegValue.Create('\SOFTWARE\Microsoft Windows\Current Version\Control Panel\MyAppl003', vtInteger);
  try
    Check(vRegValue.Root = 0);
    Check(vRegValue.Key = 'SOFTWARE\Microsoft Windows\Current Version\Control Panel');
    Check(vRegValue.Name = 'MyAppl003');
  finally
    vRegValue.Free;
  end;

  vRegValue := RegValue.Create('HKLM\SOFTWARE\Microsoft Windows\Current Version\Control Panel\MyAppl 004', vtInteger);
  try
    Check(vRegValue.Root = HKEY_LOCAL_MACHINE);
    Check(vRegValue.Key = 'SOFTWARE\Microsoft Windows\Current Version\Control Panel');
    Check(vRegValue.Name = 'MyAppl 004');
  finally
    vRegValue.Free;
  end;

  vRegValue := RegValue.Create('hkcu\SOFTWARE\Microsoft Windows\Current Version\Control Panel\My Appl 005', vtInteger);
  try
    Check(vRegValue.Root = HKEY_CURRENT_USER);
    Check(vRegValue.Key = 'SOFTWARE\Microsoft Windows\Current Version\Control Panel');
    Check(vRegValue.Name = 'My Appl 005');
  finally
    vRegValue.Free;
  end;

  vRegValue := RegValue.Create('hKu\SOFTWARE\Microsoft Windows\Current Version\Control Panel\My Appl 006', vtInteger);
  try
    Check(vRegValue.Root = HKEY_USERS);
    Check(vRegValue.Key = 'SOFTWARE\Microsoft Windows\Current Version\Control Panel');
    Check(vRegValue.Name = 'My Appl 006');
  finally
    vRegValue.Free;
  end;

  vRegValue := RegValue.Create(PUBLIC_INT_PROPERTY_NAME, PUBLIC_INT_PROPERTY_DEFAULTVALUE);
  try
    Check(vRegValue.Root = 0);
    Check(vRegValue.Key = '');
    Check(vRegValue.Name = PUBLIC_INT_PROPERTY_NAME);
    Check(vRegValue.DefValue = PUBLIC_INT_PROPERTY_DEFAULTVALUE);
  finally
    vRegValue.Free;
  end;

  vRegValue := RegValue.Create(PUBLIC_STRING_PROPERTY_NAME, PUBLIC_STRING_PROPERTY_DEFAULTVALUE);
  try
    Check(vRegValue.Root = 0);
    Check(vRegValue.Key = '');
    Check(vRegValue.Name = PUBLIC_STRING_PROPERTY_NAME);
    Check(vRegValue.DefValue = PUBLIC_STRING_PROPERTY_DEFAULTVALUE);
  finally
    vRegValue.Free;
  end;
end;

procedure TRegValueTest.Test_Loading;
var
  O: TRegistryStoreTestClass;
begin
  // first store the test values using Test_Storing
  Test_StoreDefaults;

  O := TRegistryStoreTestClass.Create;
  try
    DAttributes.WinRegistry.Load(O);

    Check(O.Property1 = PROPERTY1_DEFAULT_VALUE);
    Check(O.Prop02 = PROPERTY2_DEFAULT_VALUE);
    Check(IsEqualFloats(O.Prop_3, PROPERTY3_DEFAULT_VALUE));
  finally
    O.Free;
  end;

  Test_StoreTestValues;
  O := TRegistryStoreTestClass.Create;
  try
    DAttributes.WinRegistry.Load(O);

    Check(O.Property1 = PROPERTY1_TEST_VALUE);
    Check(O.Prop02 = PROPERTY2_TEST_VALUE);
    Check(IsEqualFloats(O.Prop_3, PROPERTY3_TEST_VALUE));
    Check(IsEqualFloats(O.Prop004, PROPERTY4_TEST_VALUE));
    Check(IsEqualFloats(O.Prop5, PROPERTY5_TEST_VALUE));
    // check that the read only property must not be changed, ie. stays in its default value
    Check(O.ReadOnlyProp1 <> READONLYPROPERTY1_TEST_VALUE);
  finally
    O.Free;
  end;
end;

procedure TRegValueTest.Test_Reset;
var
  O: TRegistryStoreTestClass;
begin
  O := TRegistryStoreTestClass.Create;
  try
    // make sure initial properties don't have same values as the defaults
    Check(O.Property1 <> PROPERTY1_DEFAULT_VALUE);
    Check(O.Prop02 <> PROPERTY2_DEFAULT_VALUE);
    Check(O.Prop_3 <> PROPERTY3_DEFAULT_VALUE);

    // property 4 and 5 have no default. Set them to test values
    O.Prop004 := PROPERTY4_TEST_VALUE;
    O.Prop5   := PROPERTY5_TEST_VALUE;

    // Reset the object to assigned default values
    DAttributes.Reset(O);

    // Now check if the object has default values
    Check(O.Property1 = PROPERTY1_DEFAULT_VALUE);
    Check(O.Prop02 = PROPERTY2_DEFAULT_VALUE);
    Check(IsEqualFloats(O.Prop_3, PROPERTY3_DEFAULT_VALUE));
    // property 4 and 5 have no default. They must not change
    Check(CompareFloats(O.Prop004, PROPERTY4_TEST_VALUE) = 0);
    Check(CompareFloats(O.Prop5,   PROPERTY5_TEST_VALUE) = 0);

    // change the properties
    O.Property1 := PROPERTY1_TEST_VALUE;
    O.Prop02    := PROPERTY2_TEST_VALUE;
    O.Prop_3    := PROPERTY3_TEST_VALUE;

    // make the properties now don't have same values as the defaults
    Check(O.Property1 <> PROPERTY1_DEFAULT_VALUE);
    Check(O.Prop02 <> PROPERTY2_DEFAULT_VALUE);
    Check(CompareFloats(O.Prop_3, PROPERTY3_DEFAULT_VALUE) <> 0);

    // Reset the object to assigned default values
    DAttributes.Reset(O);

    // Verify that the object has default values
    Check(O.Property1 = PROPERTY1_DEFAULT_VALUE);
    Check(O.Prop02 = PROPERTY2_DEFAULT_VALUE);
    Check(CompareFloats(O.Prop_3, PROPERTY3_DEFAULT_VALUE) = 0);

    // property 4 and 5 have no default. They must not change
    Check(CompareFloats(O.Prop004, PROPERTY4_TEST_VALUE) = 0);
    Check(CompareFloats(O.Prop5,   PROPERTY5_TEST_VALUE) = 0);
  finally
    O.Free;
  end;
end;

procedure TRegValueTest.Test_StoreDefaults;
var
  O: TRegistryStoreTestClass;
  vReg: TRegistry;
begin
  // test storing default values
  O := TRegistryStoreTestClass.Create;
  try
    // Reset the object
    DAttributes.Reset(O);
    // store to windows registry
    DAttributes.WinRegistry.Save(O);

    vReg := OpenRegistry(PROPERTY1_ROOTKEY, PROPERTY1_REGKEY);
    try
      Check(vReg.ValueExists(PROPERTY1_NAME), Format('Registry value with name %s does not exist', [PROPERTY1_NAME]));
      Check(vReg.ReadInteger(PROPERTY1_NAME) = PROPERTY1_DEFAULT_VALUE, Format('Property %s default value has not been stored properly', [PROPERTY1_NAME]));
    finally
      vReg.Free;
    end;

    vReg := OpenRegistry(PROPERTY2_ROOTKEY, PROPERTY2_REGKEY);
    try
      Check(vReg.ValueExists(PROPERTY2_NAME), Format('Registry value with name %s does not exist', [PROPERTY2_NAME]));
      Check(vReg.ReadString(PROPERTY2_NAME) = PROPERTY2_DEFAULT_VALUE, Format('Property %s default value has not been stored properly', [PROPERTY2_NAME]));
    finally
      vReg.Free;
    end;

    vReg := OpenRegistry(MAINROOTKEY, MAINREGKEY);
    try
      Check(vReg.ValueExists(PROPERTY3_NAME), Format('Registry value with name %s does not exist', [PROPERTY3_NAME]));
      Check(CompareFloats(vReg.ReadFloat(PROPERTY3_NAME), PROPERTY3_DEFAULT_VALUE)=0, Format('Property %s default value has not been stored properly', [PROPERTY3_NAME]));
    finally
      vReg.Free;
    end;

    vReg := OpenRegistry(READONLYPROPERTY1_ROOTKEY, READONLYPROPERTY1_REGKEY);
    try
      Check(vReg.ValueExists(READONLYPROPERTY1_NAME), Format('Registry value with name %s does not exist', [READONLYPROPERTY1_NAME]));
      Check(vReg.ReadInteger(READONLYPROPERTY1_NAME) = READONLYPROPERTY1_DEFAULT_VALUE, Format('Property %s default value has not been stored properly', [READONLYPROPERTY1_NAME]));
    finally
      vReg.Free;
    end;
  finally
    O.Free;
  end;
end;

procedure TRegValueTest.Test_StoreTestValues;
var
  O: TRegistryStoreTestClass;
  vReg: TRegistry;
begin
  // test storing default values
  O := TRegistryStoreTestClass.Create;
  try
    // Reset the object
    DAttributes.Reset(O);

    O.Property1 := PROPERTY1_TEST_VALUE;
    O.Prop02 := PROPERTY2_TEST_VALUE;
    O.Prop_3 := PROPERTY3_TEST_VALUE;
    O.Prop004 := PROPERTY4_TEST_VALUE;
    O.Prop5 := PROPERTY5_TEST_VALUE;
    O.FReadOnlyProp1 := READONLYPROPERTY1_TEST_VALUE;

    // store to windows registry
    DAttributes.WinRegistry.Save(O);

    vReg := OpenRegistry(PROPERTY1_ROOTKEY, PROPERTY1_REGKEY);
    try
      Check(vReg.ValueExists(PROPERTY1_NAME), Format('Registry value with name %s does not exist', [PROPERTY1_NAME]));
      Check(vReg.ReadInteger(PROPERTY1_NAME) = PROPERTY1_TEST_VALUE, Format('Property %s default value has not been stored properly', [PROPERTY1_NAME]));
    finally
      vReg.Free;
    end;

    vReg := OpenRegistry(PROPERTY2_ROOTKEY, PROPERTY2_REGKEY);
    try
      Check(vReg.ValueExists(PROPERTY2_NAME), Format('Registry value with name %s does not exist', [PROPERTY2_NAME]));
      Check(vReg.ReadString(PROPERTY2_NAME) = PROPERTY2_TEST_VALUE, Format('Property %s default value has not been stored properly', [PROPERTY2_NAME]));
    finally
      vReg.Free;
    end;

    vReg := OpenRegistry(MAINROOTKEY, MAINREGKEY);
    try
      Check(vReg.ValueExists(PROPERTY3_NAME), Format('Registry value with name %s does not exist', [PROPERTY3_NAME]));
      Check(CompareFloats(vReg.ReadFloat(PROPERTY3_NAME), PROPERTY3_TEST_VALUE)=0, Format('Property %s default value has not been stored properly', [PROPERTY3_NAME]));
    finally
      vReg.Free;
    end;

    vReg := OpenRegistry(MAINROOTKEY, MAINREGKEY);
    try
      Check(vReg.ValueExists(PROPERTY4_NAME), Format('Registry value with name %s does not exist', [PROPERTY4_NAME]));
      Check(CompareFloats(vReg.ReadFloat(PROPERTY4_NAME), PROPERTY4_TEST_VALUE)=0, Format('Property %s default value has not been stored properly', [PROPERTY3_NAME]));
    finally
      vReg.Free;
    end;

    vReg := OpenRegistry(MAINROOTKEY, MAINREGKEY);
    try
      Check(vReg.ValueExists(PROPERTY5_NAME), Format('Registry value with name %s does not exist', [PROPERTY5_NAME]));
      Check(CompareFloats(vReg.ReadFloat(PROPERTY5_NAME), PROPERTY5_TEST_VALUE)=0, Format('Property %s default value has not been stored properly', [PROPERTY3_NAME]));
    finally
      vReg.Free;
    end;

    vReg := OpenRegistry(READONLYPROPERTY1_ROOTKEY, READONLYPROPERTY1_REGKEY);
    try
      Check(vReg.ValueExists(READONLYPROPERTY1_NAME), Format('Registry value with name %s does not exist', [READONLYPROPERTY1_NAME]));
      Check(vReg.ReadInteger(READONLYPROPERTY1_NAME) = READONLYPROPERTY1_TEST_VALUE, Format('Property %s default value has not been stored properly', [READONLYPROPERTY1_NAME]));
    finally
      vReg.Free;
    end;
  finally
    O.Free;
  end;
end;

{ TRegRootTest }

procedure TRegRootTest.Test_Create;
var
  vRegRootKey: RegRootKey;
begin
  vRegRootKey := RegRootKey.Create(HKEY_CURRENT_USER);
  try
    Check(vRegRootKey.RootKey = HKEY_CURRENT_USER);
  finally
    vRegRootKey.Free;
  end;

  vRegRootKey := RegRootKey.Create(HKEY_LOCAL_MACHINE);
  try
    Check(vRegRootKey.RootKey = HKEY_LOCAL_MACHINE);
  finally
    vRegRootKey.Free;
  end;

  vRegRootKey := RegRootKey.Create(HKEY_CLASSES_ROOT);
  try
    Check(vRegRootKey.RootKey = HKEY_CLASSES_ROOT);
  finally
    vRegRootKey.Free;
  end;

  vRegRootKey := RegRootKey.Create(HKEY_USERS);
  try
    Check(vRegRootKey.RootKey = HKEY_USERS);
  finally
    vRegRootKey.Free;
  end;
end;

{ TRegistryStoreTestClass }

constructor TRegistryStoreTestClass.Create;
begin
  FReadOnlyProp1 := READONLYPROPERTY1_DEFAULT_VALUE;
end;

initialization
  TestFramework.RegisterTest('', TRegValueTest.Suite);
  TestFramework.RegisterTest('', TRegRootTest.Suite);
end.
